package cc.eddic.practice.concurrency.impl;

import cc.eddic.practice.concurrency.LogItem;
import cc.eddic.practice.concurrency.LogItemParser;
import cc.eddic.practice.concurrency.LogParser;
import lombok.Getter;
import lombok.val;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static cc.eddic.practice.concurrency.ExecutorServiceUtil.shutdown;

public class LogParserWithFutureScwImpl implements LogParser {
    @Override
    public Map<String, List<LogItem>> parse(String folderName) {
        Map<String, List<LogItem>> resultMap = new ConcurrentHashMap<>();
        List<Future<Model>> futures;
        ExecutorService executorService = Executors.newFixedThreadPool(64);
        try {
            val p = Paths.get(folderName);
            try (val walk = Files.walk(p)) {
                futures = walk.parallel().filter(Files::isRegularFile)
                        .filter(path -> path.getFileName().toString().endsWith(".log"))
                        .map(path -> executorService.submit(() -> parseFile(path)))
                        .collect(Collectors.toList());
            }
        } catch (IOException e) {
            return resultMap;
        }
        allof(futures);
        futures.stream().parallel().map(this::getFuture).filter(Objects::nonNull)
                .forEach(x -> resultMap.put(x.getPath().toString(), x.getItems()));
        shutdown(executorService);
        return resultMap;
    }

    private static <T> void allof(final List<Future<T>> futures) {
        for (Future<T> future : futures) {
            try {
                future.get(); // This blocks until the future completes
            } catch (InterruptedException | ExecutionException e) {
                // Handle exceptions as needed
            }
        }
    }

    private Model getFuture(Future<Model> future) {
        try {
            return future.get();
        } catch (ExecutionException | InterruptedException e) {
            return null;
        }
    }

    protected Model parseFile(Path path) {
        try (val lines = Files.lines(path)) {
            LogItemParser logItemParser = new LogItemParserScwImpl();
            val items = lines.map(logItemParser::parse)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .collect(Collectors.toList());
            return new Model(path, items);
        } catch (IOException ex) {
            return new Model(path);
        }
    }

    @Getter
    protected static class Model {
        public Model(Path path) {
            this(path, new ArrayList<>());
        }

        public Model(Path path, List<LogItem> items) {
            this.path = path;
            this.items = items;
        }

        private final Path path;
        private final List<LogItem> items;
    }
}

